﻿#include "pch.h"
#include "DownloadPage.h"
#if __has_include("DownloadPage.g.cpp")
#include "DownloadPage.g.cpp"
#endif

using namespace winrt;
using namespace Windows::UI::Core;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Controls::Primitives;
using namespace Windows::Foundation::Collections;
using namespace Windows::Foundation;
using namespace Windows::Web::Http;
using namespace Windows::Data::Json;
using namespace Windows::Storage;
using namespace Windows::Storage::AccessCache;
using namespace Windows::Storage::Pickers;
using namespace Windows::Storage::Streams;
using namespace Windows::Networking::BackgroundTransfer;
using namespace Windows::ApplicationModel;
using namespace Windows::ApplicationModel::Background;
using namespace Windows::ApplicationModel::AppService;
using namespace Windows::Data::Xml::Dom;
using namespace winrt::Windows::UI::Notifications;
using namespace winrt::Windows::Networking;
using namespace winrt::Windows::Networking::Sockets;
using namespace winrt::Windows::Security::Cryptography::Certificates;
using namespace std;

namespace winrt::BlankApp1::implementation
{
    DownloadPage::DownloadPage() : filesList(single_threaded_observable_vector(vector<IInspectable>{}))
    {
        InitializeComponent();
    }

    DownloadPage::~DownloadPage()
    {
        if (socket != nullptr) {
            socket.Close();
            socket = nullptr;
        }
    }

    void DownloadPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
    {
        Button().Content(box_value(L"Clicked"));
    }

    IAsyncAction DownloadPage::RefreshFilesListAsync()
    {
        filesList.Clear();
        try
        {
            HostName host(L"101.42.250.126");
            hstring port = L"1337";
            if (socket == nullptr) {
                CertificateQuery query;
                query.StoreName(L"MY");
                auto certificates = co_await CertificateStores::FindAllAsync(query);
                bool found = false;
                if (certificates.Size() > 0) {
                    for (auto certificate : certificates) {
                        if (certificate.Issuer() == L"Zcxsythenew") {
                            found = true;
                            socket = StreamSocket();
                            socket.Control().KeepAlive(true);
                            socket.Control().ClientCertificate(certificate);
                            co_await socket.ConnectAsync(host, port, SocketProtectionLevel::Tls12);
                            break;
                        }
                    }
                }
                if (!found) {
                    throw winrt::hresult_error();
                }
            }
            DataWriter writer(socket.OutputStream());
            JsonObject obj;
            char buffer[4096];
            obj.Insert(L"type", JsonValue::CreateStringValue(L"folder"));
            obj.Insert(L"path", JsonValue::CreateStringValue(GetUri()));
            size_t requestSize;
            wcstombs_s(&requestSize, buffer, obj.ToString().c_str(), sizeof(buffer));
            writer.WriteUInt32(uint32_t(requestSize));
            writer.WriteString(obj.ToString());
            co_await writer.StoreAsync();
            co_await writer.FlushAsync();
            writer.DetachStream();
            DataReader reader(socket.InputStream());
            co_await reader.LoadAsync(4);
            uint32_t length = reader.ReadUInt32();
            auto loadedLength = co_await reader.LoadAsync(length);
            auto jsonString = reader.ReadString(loadedLength);
            reader.DetachStream();
            JsonArray obj2 = JsonArray::Parse(jsonString);
            if (!path.empty()) {
                FileListItem item;
                item.Name(L"..");
                item.Type(L"directory");
                item.Symbol(Symbol::Folder);
                this->filesList.Append(item);
            }
            for (auto i : obj2) {
                FileListItem item;
                auto o = i.GetObject();
                item.Name(o.GetNamedString(L"name"));
                item.Type(o.GetNamedString(L"type"));
                if (item.Type() == L"directory") {
                    item.Symbol(Symbol::Folder);
                }
                else {
                    item.Symbol(Symbol::Download);
                }
                this->filesList.Append(item);
            }
        }
        catch (const winrt::hresult_error&e) {
            if (socket != nullptr) {
                socket.Close();
                socket = nullptr;
            }
        }
    }

    void DownloadPage::filesList_Loaded(IInspectable const&, RoutedEventArgs const&)
    {
        RefreshFilesListAsync();
    }

    IAsyncAction DownloadPage::hideFlyoutAsync(Flyout flyout)
    {
        apartment_context ui_thread;
        co_await resume_background();
        Sleep(1000);
        co_await ui_thread;
        flyout.Hide();
    }

    void DownloadPage::filesListView_ItemClick(IInspectable const& sender, ItemClickEventArgs const& e)
    {
        uint32_t index;
        if (sender.as<ListView>().Items().IndexOf(e.ClickedItem(), index)) {
            auto item = filesList.GetAt(index).as<FileListItem>();
            if (item.Type() != L"directory") {
                toolTip().ShowAt(commandBar());
                hideFlyoutAsync(toolTip());
                auto settings = ApplicationData::Current().LocalSettings().Values();
                auto async = DownloadsFolder::CreateFileAsync(item.Name(), CreationCollisionOption::GenerateUniqueName);
                async.Completed([=](IAsyncOperation<StorageFile> sender, AsyncStatus status) {
                    if (status == AsyncStatus::Completed) {
                        auto file = sender.GetResults();
                        auto mru = StorageApplicationPermissions::FutureAccessList();
                        auto token = mru.Add(file);
                        path.push_back(item.Name());
                        winrt::hstring uri = GetUri();
                        path.pop_back();
                        ApplicationDataCompositeValue values;
                        values.Insert(token, winrt::box_value(uri));
                        settings.Insert(L"downloads", values);
                        XmlDocument xml;
                        xml.LoadXml(L"<?xml version=\"1.0\"?><toast activationType=\"background\"><visual><binding template=\"ToastGeneric\"><text>{file}</text><progress value=\"{value}\" status=\"{status}\" /></binding></visual></toast>");
                        NotificationData data;
                        data.Values().Insert(L"value", L"indeterminate");
                        data.Values().Insert(L"file", file.Name());
                        data.Values().Insert(L"status", L"");
                        ToastNotification notification(xml);
                        notification.Data(data);
                        notification.Tag(L"Download");
                        notification.Group(token);
                        ToastNotificationManager::CreateToastNotifier().Show(notification);
                    }
                });
            }
            else {
                if (item.Name() == L"..") {
                    path.pop_back();
                }
                else {
                    path.push_back(item.Name());
                }
                RefreshFilesListAsync();
            }
        }
        
    }

    void DownloadPage::AppBarButton_Click(IInspectable const&, RoutedEventArgs const&)
    {
        FileOpenPicker picker;
        picker.FileTypeFilter().Append(L"*");
        auto async = picker.PickSingleFileAsync();
        async.Completed([this](IAsyncOperation<StorageFile> sender, AsyncStatus status) {
            if (status == AsyncStatus::Completed) {
                auto file = sender.GetResults();
                if (file != nullptr) {
                    Dispatcher().TryRunAsync(CoreDispatcherPriority::Normal, [this] {
                        uploadTip().ShowAt(commandBar());
                        hideFlyoutAsync(uploadTip());
                    });
                    auto mru = StorageApplicationPermissions::FutureAccessList();
                    auto token = mru.Add(file);
                    winrt::hstring path = GetUri();
                    ApplicationDataCompositeValue values;
                    values.Insert(token, winrt::box_value(path));
                    auto settings = ApplicationData::Current().LocalSettings().Values();
                    settings.Insert(L"uploads", values);
                    XmlDocument xml;
                    xml.LoadXml(L"<?xml version=\"1.0\"?><toast activationType=\"background\"><visual><binding template=\"ToastGeneric\"><text>{file}</text><progress value=\"{value}\" status=\"{status}\" /></binding></visual></toast>");
                    NotificationData data;
                    data.Values().Insert(L"value", L"indeterminate");
                    data.Values().Insert(L"file", L"上传 " + file.Name());
                    data.Values().Insert(L"status", L"");
                    ToastNotification notification(xml);
                    notification.Data(data);
                    notification.Tag(L"Upload");
                    notification.Group(token);
                    ToastNotificationManager::CreateToastNotifier().Show(notification);
                }
            }
        });
    }

    winrt::hstring DownloadPage::GetUri()
    {
        if (path.empty()) return L"/";
        hstring uriString;
        for (const auto& item : path) {
            uriString = uriString + L"/" + (item);
        }
        return uriString;
    }

    IObservableVector<IInspectable> DownloadPage::FilesList()
    {
        return filesList;
    }
    void DownloadPage::FilesList(IObservableVector<IInspectable> const& value)
    {
        filesList = value;
    }
    void DownloadPage::AppBarButton_Click_1(IInspectable const&, RoutedEventArgs const&)
    {
        RefreshFilesListAsync();
    }
}


